#include "dspSimulator/instruction/CMPEQ.h"

namespace dspsimulator {
    std::shared_ptr<DspInstruction> CMPEQ::decode_standard_instruction(const std::shared_ptr<DspInstructionInfo> &dsp_instruction_info) {
        std::shared_ptr<CMPEQ> cmpeq;
        std::bitset<32> &data = dsp_instruction_info->m_standard_bits;
        if (exist_standard_condition(common::extract_bits<4>(data, 28)) && common::check_bits<3>(data, 0b110, 2) &&
            common::check_bits<7>(data, 0b1010010, 5)) {
            cmpeq = std::make_shared<CMPEQ>(dsp_instruction_info->address(), 4);
            cmpeq->m_creg_z = common::extract_bits<4>(data, 28);
            bool x = data[12];
            bool s = data[1];
            cmpeq->m_p = data[0];
            u32 src1 = common::extract_bit_to_int<5>(data, 13);
            u32 src2 = common::extract_bits<5>(data, 18).to_ulong();
            u32 dst = common::extract_bits<5>(data, 23).to_ulong();
            cmpeq->m_op1 = std::make_shared<Immediate32>(src1);
            cmpeq->m_op2 = std::make_shared<SingleReg>(get_src2_reg32_kind(src2, s, x));
            cmpeq->m_op3 = std::make_shared<SingleReg>(get_dst_reg32_kind(dst, s));
        }
        return cmpeq;
    }

    void CMPEQ::generate_ir(const std::shared_ptr<simulator::IRGenElement> &ir_gen_element) {
        std::shared_ptr<simulator::LLVMJit> llvm_jit = ir_gen_element->m_llvm_jit;
        RegIRCopy &reg_ir_copy = ir_gen_element->m_reg_ir_copy;
        llvm::Function *function = ir_gen_element->m_function;
        std::shared_ptr<llvm::IRBuilder<>> ir_builder = llvm_jit->ir_builder();
        // CMPEQ imm32,src2,dst
        auto op1 = std::dynamic_pointer_cast<Immediate32>(m_op1);
        auto op2 = std::dynamic_pointer_cast<SingleReg>(m_op2);
        auto op3 = std::dynamic_pointer_cast<SingleReg>(m_op3);
        llvm::LLVMContext *llvm_context = llvm_jit->get_context_ptr();
        if (op1 != nullptr && op2 != nullptr && op3 != nullptr) {
            llvm::Value *src1 = ir_builder->getInt32(op1->data());
            std::string src2_name = get_reg_name(op2->reg());
            llvm::Value *src2 = reg_ir_copy[src2_name];
            std::string dst_name = get_reg_name(op3->reg());
            llvm::Value *dst_ptr = IRGenerator::get_global_value_ptr(llvm_jit, dst_name);
            llvm::Value *condition = ir_builder->CreateICmpEQ(src1, src2);
            llvm::BasicBlock *if_bb = llvm::BasicBlock::Create(*llvm_context, "if", function);
            llvm::BasicBlock *else_bb = llvm::BasicBlock::Create(*llvm_context, "else", function);
            llvm::BasicBlock *end_bb = llvm::BasicBlock::Create(*llvm_context, "end", function);
            ir_builder->CreateCondBr(condition, if_bb, else_bb);
            ir_builder->SetInsertPoint(if_bb);
            ir_builder->CreateStore(ir_builder->getInt32(1), dst_ptr);
            ir_builder->CreateBr(end_bb);
            ir_builder->SetInsertPoint(else_bb);
            ir_builder->CreateStore(ir_builder->getInt32(0), dst_ptr);
            ir_builder->CreateBr(end_bb);
            ir_builder->SetInsertPoint(end_bb);
        }
    }

    std::string CMPEQ::to_string() const {
        return get_standard_condition(m_creg_z) + m_name + " " + get_function_unit_name(m_func_unit) + " " +
               m_op1->to_string() + "," + m_op2->to_string() + "," + m_op3->to_string();
    }
}